﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Pixysoft.Framework.Verifications;
using Pixysoft.Framework.Sockets.Core;

namespace Pixysoft.Framework.Sockets.Codec
{
    public class SocketSessionCodecFactory : ISessionCodecFactory
    {
        public ISessionDecoder CreateDecoder()
        {
            return new SocketSessionDecoder();
        }

        public ISessionEncoder CreateEncoder()
        {
            return new SocketSessionEncoder();
        }
    }

    class SocketSessionDecoder : ISessionDecoder
    {
        SocketMessageReceiveQueue queue = new SocketMessageReceiveQueue();

        public bool DoDecode(ISocketIoSession socketIoSession, byte[] socketBuffer, int recvSize, int availableSize, out List<IProtocolMessage> messages)
        {
            messages = new List<IProtocolMessage>();

            try
            {
                if (!queue.Append(socketBuffer, recvSize, availableSize))
                {
                    return false;
                }

                while (!socketIoSession.IsDisposed && queue.ContainMessages())
                {
                    ProtocolMessage message = new ProtocolMessage();

                    byte[] messageBytes = queue.NextMessage();

                    using (BinaryReader reader = new BinaryReader(StreamCoder.Instance.ToStream(messageBytes)))
                    {
                        message.Protocol = reader.ReadInt32();
                        message.Action = reader.ReadInt32();
                        message.Sn = reader.ReadInt32();
                        message.Status = reader.ReadInt32();

                        if (reader.BaseStream.Position < reader.BaseStream.Length - 1)
                        {
                            byte[] body = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position));

                            message.ByteBody = body;
                        }

                        messages.Add(message);
                    }
                }

                return true;
            }
            catch (Exception ex)
            {
                LoggerHelper.Fatal(ex);

                return false;
            }
        }
    }

    class SocketSessionEncoder : ISessionEncoder
    {
        public bool DoEncode(ISocketIoSession socketIoSession, IProtocolMessage[] messages, out byte[] sendBytes)
        {
            sendBytes = new byte[] { };

            try
            {
                using (BinaryWriter writer = new BinaryWriter(new MemoryStream()))
                {
                    foreach (IProtocolMessage message in messages)
                    {
                        writer.Write(StringHelper.default_header);
                        writer.Write(StringHelper.default_packageprotocollength + message.ByteBody.Length);

                        writer.Write(message.Protocol);
                        writer.Write(message.Action);
                        writer.Write(message.Sn);
                        writer.Write(message.Status);

                        if (message.ByteBody.Length > 0)
                        {
                            writer.Write(message.ByteBody, 0, message.ByteBody.Length);
                        }

                        LoggerHelper.Debug("{0}::socket finish send message, protocol:{1}, action:{2}",//
                                socketIoSession.Identifier, message.Protocol, message.Action);
                    }

                    if (writer.BaseStream.Length <= 0)
                    {
                        return true;
                    }

                    //writer.Write((byte)0x0);//2011-01-03 不添加分隔符 提高性能！！！

                    sendBytes = (writer.BaseStream as MemoryStream).ToArray();

                    return true;
                }
            }
            catch (Exception ex)
            {
                LoggerHelper.Fatal(ex);

                return false;
            }
        }

    }

    /// <summary>
    /// 当没有完全接受一个信息头，则在stream继续接受
    /// 当完全接受了信息头之后，剩余的直接压入stream
    /// 当stream存在了一个可用的message，直接取出压入messages，然后进一步迭代write.
    /// </summary>
    class SocketMessageReceiveQueue : ISocketMessageReceiveQueue
    {
        private const int int_reset = -1;

        private const int int_zero = 0;

        bool isReceiveHeader = false;

        bool isReceiveLength = false;

        int streamTotalLength = int_reset;//包总长度 根据协议获取

        int streamLength = int_reset;//当前有效长度

        MemoryStream stream = new MemoryStream();//正在接受的buffer

        Queue<byte[]> messages = new Queue<byte[]>();//完成的messages


        /// <summary>
        /// 添加字符串
        /// 如果 出现异常，则返回false
        /// </summary>
        /// <param name="socketBuffer"></param>
        /// <param name="recvSize"></param>
        /// <returns></returns>
        public bool Append(byte[] socketBuffer, int recvSize, int availableSize)
        {
            byte[] content = null;

            try
            {
                content = Prepare(socketBuffer, recvSize);
            }
            catch (Exception ex)
            {
                LoggerHelper.Fatal(ex);
                return false;
            }

            if (content.Length == 0)
                return true;

            int currentLength = this.streamLength + content.Length;

            // still not ready

            if (currentLength < this.streamTotalLength)
            {
                this.streamLength = currentLength;
                this.isReceiveLength = true;
                this.isReceiveHeader = true;// still wait for next byte[]
                WriteToStream(content, 0, content.Length, stream);

                return true;
            }

            // more than one message arrive

            int preLength = this.streamTotalLength - this.streamLength;
            int leftLength = currentLength - this.streamTotalLength;

            WriteToStream(content, 0, preLength, stream);
            byte[] message = stream.ToArray();
            messages.Enqueue(message);

            this.streamLength = int_reset;
            this.streamTotalLength = int_reset;
            this.isReceiveLength = false;
            this.isReceiveHeader = false;// finish current package
            ClearStream();

            if (leftLength <= 0)
                return true;

            byte[] _rbuffer = new byte[leftLength];
            Buffer.BlockCopy(content, preLength, _rbuffer, 0, leftLength);
            return Append(_rbuffer, _rbuffer.Length, availableSize);
        }

        /// <summary>
        /// 使用stream接受临时的bytes，如果已经存在抱头，则返回；否则获取包头
        /// </summary>
        /// <param name="socketBuffer"></param>
        /// <param name="recvSize"></param>
        /// <returns></returns>
        private byte[] Prepare(byte[] socketBuffer, int recvSize)
        {
            // 如果已经获取了包头，则直接返回当前接受的buffer

            if (this.isReceiveHeader && this.isReceiveLength)
            {
                byte[] _rbuffer = new byte[recvSize];
                Buffer.BlockCopy(socketBuffer, 0, _rbuffer, 0, _rbuffer.Length);
                return _rbuffer;
            }

            //倒退1个头长度搜索header/length

            WriteToStream(socketBuffer, 0, recvSize, stream);
            byte[] content = stream.ToArray();
            ClearStream();
            int offset = 0;
            int length = content.Length;

            //开始搜索header

            using (BinaryReader instreamReader = new BinaryReader(StreamCoder.Instance.ToStream(content)))
            {
                while (length >= StringHelper.default_intlength && !this.isReceiveHeader)
                {
                    int header = instreamReader.ReadInt32();

                    offset += StringHelper.default_intlength;

                    length -= StringHelper.default_intlength;

                    if (header != StringHelper.default_header)
                    {
                        offset = offset - StringHelper.default_intlength + 1;

                        length = length + StringHelper.default_intlength - 1;

                        instreamReader.BaseStream.Position = offset;

                        continue;
                    }

                    this.isReceiveHeader = true;

                    break;
                }

                if (!this.isReceiveHeader)
                {
                    if (content.Length > StringHelper.default_maxlength)
                    {
                        throw Exceptions.ManualVerificationFailedException("receive error header content, length = " + content.Length);
                    }

                    if (content.Length - StringHelper.default_intlength >= 0)
                    {
                        offset = content.Length - StringHelper.default_intlength + 1;
                    }
                    else
                    {
                        offset = 0;
                    }

                    length = content.Length - offset;

                    LoggerHelper.Debug("still missing package header, wait for next loop");

                    WriteToStream(content, offset, length, stream);

                    return new byte[] { };
                }

                // if no length, get length

                if (length < StringHelper.default_intlength)
                {
                    LoggerHelper.Debug("package length is still not ready, wait for next loop.");

                    WriteToStream(content, offset, length, stream);

                    return new byte[] { };
                }

                this.streamTotalLength = instreamReader.ReadInt32();
                this.streamLength = int_zero;
                this.isReceiveLength = true;

                // return value

                offset += StringHelper.default_intlength;
                length -= StringHelper.default_intlength;

                if (length <= 0)
                    return new byte[] { };

                byte[] _rbuffer2 = new byte[content.Length - offset];
                Buffer.BlockCopy(content, offset, _rbuffer2, 0, _rbuffer2.Length);
                return _rbuffer2;
            }
        }

        private void WriteToStream(byte[] buffer, int offset, int length, Stream stream)
        {
            BinaryWriter writer = new BinaryWriter(stream);
            {
                writer.Write(buffer, offset, length);
            }
        }

        private void ClearStream()
        {
            this.stream.Close();
            this.stream.Dispose();
            this.stream = new MemoryStream();
        }


        public bool ContainMessages()
        {
            return messages.Count > 0;
        }

        public byte[] NextMessage()
        {
            return messages.Dequeue();
        }

        public void ClearAll()
        {
            this.streamTotalLength = int_reset;
            this.streamLength = int_reset;
            this.stream.Close();
            this.stream.Dispose();
            this.stream = new MemoryStream();
            messages.Clear();
        }
    }
}
